import random
from typing import Protocol, Optional
from base import Pairing, BoardState
from enum import Enum

class ValueType(Enum):
    disabled = 0
    required = 1
    optional = 2

class DiceValue(int):
    type: ValueType
    def __new__(cls, value: int, value_type: ValueType):
        instance = super().__new__(cls, value)
        instance.type = value_type
        return instance

type MarkedPairing = tuple[DiceValue, DiceValue]

def is_valid_pairing(pairing: MarkedPairing) -> bool:
    a, b = pairing
    return not ((a.type is ValueType.disabled) and (b.type is ValueType.disabled))

def get_valid_pairings(pairings: tuple[Pairing, Pairing, Pairing], board_state: BoardState) -> list[MarkedPairing]:
    marked_pairings = [form_marked_pairing(pairing, board_state) for pairing in pairings]
    return [pairing for pairing in marked_pairings if is_valid_pairing(pairing)]

def get_value_type(dice_value: int, board_state: BoardState) -> ValueType:
    if dice_value in board_state.finished_columns:
        return ValueType.disabled
    elif dice_value in board_state.actice_columns:
        return ValueType.required
    elif len(board_state.actice_columns) == 3:
        return ValueType.disabled
    else:
        return ValueType.optional

def form_marked_pairing(pairing: Pairing, board_state: BoardState) -> MarkedPairing:
    a, b = pairing
    return (DiceValue(a, get_value_type(a, board_state)), DiceValue(b, get_value_type(b, board_state)))


class PairingDecisionDelegate(Protocol):
    def __call__(self, pairings: tuple[Pairing, Pairing, Pairing], board_state: BoardState) -> Optional[MarkedPairing]:
        pass

class ValueDecisionDelegate(Protocol):
    def __call__(self, pairing: MarkedPairing, board_state: BoardState) -> list[int]:
        pass

#region pairing

def pairing_random_decision(pairings: tuple[Pairing, Pairing, Pairing], board_state: BoardState) -> Optional[MarkedPairing]:
    valid_pairings = get_valid_pairings(pairings, board_state)
    return random.choice(valid_pairings) if valid_pairings else None

#endregion


#region value

def value_random_decision(pairing: MarkedPairing, board_state: BoardState) -> list[int]:
    required: list[int] = []
    optional: list[int] = []
    for dice_value in pairing:
        match dice_value.type:
            case ValueType.required:
                required.append(dice_value)
            case ValueType.optional:
                optional.append(dice_value)
            case _:
                continue

    match len(optional):
        case 1:
            if len(board_state.actice_columns) != 3:
                required.append(optional[0])
        case 2:
            if len(board_state.actice_columns) <= 1:
                required = optional
            elif len(board_state.actice_columns) == 2:
                required.append(random.choice(optional))

    return required
    

#endregion

